// Validator.h
#ifndef VALIDATOR_H
#define VALIDATOR_H

#include <vector>
#include <unordered_map>
#include <mutex>
#include "../transaction/Operation.h"
#include "../transaction/Transaction.h"

class Validator {
public:
    // Record the order of transactions for validation
    void recordTransaction(const Transaction& transaction) {
        std::lock_guard<std::mutex> guard(mutex_);
        transactionLog_.push_back(transaction);
    }

    // Validate if the recorded transactions are serializable
    bool validateSerializability() {
        std::lock_guard<std::mutex> guard(mutex_);

        // Step 1: Build the serialization graph
        buildSerializationGraph();

        // Step 2: Check for cycles in the graph
        return !hasCycle();
    }

    // Print the transaction log (for debugging)
    void printTransactionLog() const {
        for (const auto& transaction : transactionLog_) {
            transaction.printTransaction();
        }
    }

private:
    std::vector<Transaction> transactionLog_; // Log of all transactions executed
    std::unordered_map<int, std::vector<int>> graph_; // Serialization graph (adjacency list)
    std::mutex mutex_; // Mutex for thread-safe access to the transaction log

    // Build the serialization graph based on transaction conflicts
    void buildSerializationGraph() {
        graph_.clear();

        // Compare each pair of transactions to detect conflicts
        for (size_t i = 0; i < transactionLog_.size(); ++i) {
            for (size_t j = i + 1; j < transactionLog_.size(); ++j) {
                if (hasConflict(transactionLog_[i], transactionLog_[j])) {
                    graph_[transactionLog_[i].getTransactionID()].push_back(transactionLog_[j].getTransactionID());
                }
            }
        }
    }

    // Check if there is a conflict between two transactions
    bool hasConflict(const Transaction& t1, const Transaction& t2) {
        for (const auto& op1 : t1.getOperations()) {
            for (const auto& op2 : t2.getOperations()) {
                if (op1.getRowID() == op2.getRowID() &&
                    ((op1.isWrite() || op2.isWrite()))) {
                    return true;
                }
            }
        }
        return false;
    }

    // Check for cycles in the serialization graph (indicating non-serializability)
    bool hasCycle() {
        std::unordered_map<int, bool> visited;
        std::unordered_map<int, bool> stack;

        for (const auto& node : graph_) {
            if (!visited[node.first] && isCyclic(node.first, visited, stack)) {
                return true;
            }
        }
        return false;
    }

    // Depth-first search to detect cycles in the graph
    bool isCyclic(int node, std::unordered_map<int, bool>& visited, std::unordered_map<int, bool>& stack) {
        if (!visited[node]) {
            visited[node] = stack[node] = true;

            for (int neighbor : graph_[node]) {
                if (!visited[neighbor] && isCyclic(neighbor, visited, stack)) {
                    return true;
                } else if (stack[neighbor]) {
                    return true;
                }
            }
        }
        stack[node] = false;
        return false;
    }
};

#endif
